
package redeS.visualizacao;

import java.io.File;
import java.util.Formatter;
import java.util.LinkedList;
import peersim.cdsim.CDState;
import peersim.config.Configuration;
import peersim.core.CommonState;
import peersim.core.Control;

/**
 * Classe observadora da rede. Recebe dados de qualquer classe e os processa adequadamente.
 * @author thiago-pc
 */
public class P2PObserver implements Control{
	
	private static boolean turnOn = false;
	private static LinkedList<String> text0 = null, texts = null;
	private static LinkedList<Float> value0 = null ,values = null;
	
	private static LinkedList<Float> medias;
	private static LinkedList<Integer> mediaindex;
	
	public static String PAR_TITLE = "title";
	private String title;
	
	public P2PObserver(String prefix){
		turnOn = true;
		text0 = new LinkedList<String>();
		texts = new LinkedList<String>();
		value0 = new LinkedList<Float>();
		values = new LinkedList<Float>();
		medias = new LinkedList<Float>();
		mediaindex = new LinkedList<Integer>();
		
		for(int i=0; i < P2PObserverConstants.values().length; i++){
			texts.add(null);
			values.add(null);
		}
		title = Configuration.getString(prefix + "." + title,null);
	}
	
	/**
	 * Insire dados para a classe observadora. Envie pelo menos dois parâmetros: type e outro.
	 * Envie null para descartar um dos dois parâmetros finais, se quiser.</br>
	 * Veja o comentário explicativo sobre os IDs em {@link P2PObserverConstants}
	 * @param type Tipo dos dados (parâmetro obrigatório). Define como será processado.
	 * @param value Valor do dado (parâmetro ocasional). É usado na
	 * maioria dos processamentos.
	 * @param information String que acompanha o dado (parâmetro opcional).
	 */
	public static void setData(P2PObserverConstants type, Float value, String information ){
		if(turnOn){
			switch (type.ID){
			case 0:
				text0.add(information);
				value0.add(value);
				break;
			case 1:
				if(values.get(type.ordinal()-1) == null || values.get(type.ordinal()-1) < value){
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value);
				}
				break;
			case 2:
				if(values.get(type.ordinal()-1) == null || values.get(type.ordinal()-1) > value){
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value);
				}
				break;
			case 3:
				if(values.get(type.ordinal()-1) == null){
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value);
				}
				else{
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, values.get(type.ordinal()-1) + value);
				}
				break;
			case 4:
				if(values.get(type.ordinal()-1) == null){
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value);
				}
				else{
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value - values.get(type.ordinal()-1));
				}
				break;
			case 5:
				if( !mediaindex.contains(type.ordinal() - 1)){
					mediaindex.add(type.ordinal() - 1);
					medias.add((float)0);
				}
				medias.set(mediaindex.indexOf(type.ordinal() - 1),
						medias.get(mediaindex.indexOf(type.ordinal() - 1)) + 1);
				
				if(values.get(type.ordinal()-1) == null){
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, value);
				}
				else{
					texts.set(type.ordinal()-1, information);
					values.set(type.ordinal()-1, (values.get(type.ordinal()-1) + value));
				}
				break;
			}
		}
	}

	@Override
	public boolean execute() {
		if(CDState.getCycle() == CommonState.getEndTime() - 1){
			File dir, results;
			Formatter output;
			
			for(int i=0; i<mediaindex.size(); i++){
				values.set(mediaindex.get(i),
						values.get(mediaindex.get(i)) / medias.get(i));
			}
			
			System.out.println(this.getClass().getSimpleName() + ": escrevendo arquivo...");
			
			// Define o diretório de saída.
			dir = new File(this.getClass().getPackage().getName());
			dir = new File(this.getClass().getPackage().getName());
			if(!dir.isDirectory()){
				System.out.println("\t\tdiretório " + dir.getPath() + " não encontrado: "+
						"criando diretório...");
				if(dir.mkdir()){
					System.out.println("\t\tdiretório criado com sucesso.");
				}
				else{
					System.out.println("\t\terro ao criar diretório");
					return true;
				}
			}
			
			// Define o arquivo de saída.
			int resultIndex = 0;
			do{
				results = new File(dir.getPath() + "/exp_" + resultIndex + "_resultado.txt");
				resultIndex++;
			}while(results.isFile());
			
			try {
				output = new Formatter(results);
			} catch (Exception e) {
				e.printStackTrace();
				return true;
			}
			
			if(title!=null){
				output.format(title + "\n\n");
			}
			
			// Escreve no arquivo de saída os dados de ID 0
			// String1 Valor1.
			// String2 Valor2.
			// ...
			// StringN ValorN.
			for(int i=0; i<text0.size(); i++){
				output.format("%s %s.\n", text0.get(i)==null? "" : text0.get(i),
						value0.get(i)==null? "" : String.format("%.1f", value0.get(i)));
			}
			
			if(text0.size()>0) output.format("\n\n");
			
			// Escreve no arquivo os demais dados
			// TítuloConstante1: Valor1.
			// 		String1.
			// TítuloConstante2: Valor2.
			// 		String2.
			// ...
			P2PObserverConstants[] consts = P2PObserverConstants.values();
			for(int i=0; i<consts.length -1; i++){
				if(values.get(i)!=null || texts.get(i)!=null){
					output.format("%s: %s%s\n\n", consts[i+1].title, values.get(i)==null? "" :
						String.format("%.1f", values.get(i)) + ".", texts.get(i)==null? "" :
							"\n\t" + texts.get(i));
				}
			}
			
			System.out.println("\t\tescreveu arquivo " + results.getName() + " com sucesso");
			output.close();
		}
		return false;
	}
}
